/*
 * taskMgr.c
 *
 *  Created on: Apr 19, 2015
 *      Author: mxw
 */
#include <taskMgr.h>
#include <commlib.h>
#include <LedDrv.h>
#include <UartDrv.h>
#include <svc.h>

#define MAX_TASK_COUNT          (32)
#define MAX_LEN_TSK_DISCRIPT    (32)
#define IVALID_TASK_ID          (0xFF)

#define ST_TASK_INVALID     (0)
#define ST_TASK_WAIT_RUN    (1)
#define ST_TASK_SLEEP       (2)
#define ST_TASK_RUNNING     (3)

typedef struct {
    //unsigned int regs[13];//r0-r12
    unsigned int r0;
    unsigned int r1;
    unsigned int r2;
    unsigned int r3;
    unsigned int r4;
    unsigned int r5;
    unsigned int r6;
    unsigned int r7;
    unsigned int r8;
    unsigned int r9;
    unsigned int r10;
    unsigned int r11;
    unsigned int r12;

    unsigned int sp_r13;
    unsigned int lr_r14;
    unsigned int pc_r15;
    unsigned int cpsr;
} task_regs;

typedef struct {
    task_regs r;
    TASK_ENTRY_FUN entry;
    void* tsk_param;
    char tsk_name[MAX_LEN_TSK_DISCRIPT];
    unsigned char is_first_run;
    unsigned char priority;
    unsigned char tsk_st;
    unsigned int prio_rate;
    unsigned int scheduled_cnt;    
} task_info;

typedef struct _task_list_st_ {
    int tsk_cnt;
    unsigned char task_list[MAX_TASK_COUNT]; 
} tsk_list_st;

typedef struct {
    task_info arr[MAX_TASK_COUNT];
    int valid_tsk_cnt;
    tsk_list_st ready_task_list;
    tsk_list_st unready_task_list;  //can do task schedule
    unsigned int sum_prio;
    unsigned int sum_scheduled_cnt; 
} task_manager;


/*
        0x3400 0000 +--------------------------------+ 64M
        size:512k   |         task[0] stack          |
        0x33f8 0000 +--------------------------------+ 63M
        size:512k   |         task[1] stack          |
        0x33f0 0000 +--------------------------------+ 63M
                    |                                |
                    |         ...                    |
                    |                                |
        0x3308 0000 +--------------------------------+ 48M
        size:512k   |         task[31] stack         |
        0x3300 0000 +--------------------------------+ 48M
        size:512k   |         irq stack              |
        0x32f8 0000 +--------------------------------+ 47M
        size:512k   |         data abort stack       |
        0x32f0 0000 +--------------------------------+ 47M
                    |                                |
                    |                                |
        size:31.5M  |         heap                   |
                    |                                |
                    |                                |
        0x3100 0000 +--------------------------------+ 16M
          size:16M  |         code + data            |
        0x3000 0000 +--------------------------------+ 0
                    |         invalid                |
             0x1000 +--------------------------------+
            size:4k |         guide code             |
               0x0  +--------------------------------+
 */

extern void set_task_sp(int);

task_manager g_tsk_mgr;
task_regs* g_pcurrent_regs = 0;
int g_current_tsk_id = 0;

extern void restore_run(void* pr);
extern void restore_run2(void* pr);

extern int test_dis1(void* p);
extern int test_dis2(void* p);
void test_stmdb1() {
    int arr[8] = {0}; 
    int ret = 0;
    int i = 0;

    printf("\r\nbefore call test_dis1\r\n");
    printf("arr addr         val\r\n");
    for(i = 7; i >= 0; --i) {
        printf("[%d] 0x%X: 0x%X\r\n", i, (int)&arr[i], arr[i]);
    }
    i = 0;
    ret = test_dis1(&arr[3]);

    printf("\r\nafter call test_dis1\r\n");
    printf("arr addr         val\r\n" );
    for(i = 7; i >= 0; --i) {
        printf("[%d] 0x%X: 0x%X\r\n", i, (int)&arr[i], arr[i]);
    }
    printf("r0(return value):0x%X\r\n", ret);
    printf("-------------------------------------\r\n"); 
}

void test_stmdb2() {
    int arr[8] = {0}; 
    int ret = 0;
    int i = 0;

    printf("\r\nbefore call test_dis2\r\n");
    printf("arr addr         val\r\n");
    for(i = 7; i >= 0; --i) {
        printf("[%d] 0x%X: 0x%X\r\n", i, (int)&arr[i], arr[i]);
    }
    i = 0;
    ret = test_dis2(&arr[3]);

    printf("\r\nafter call test_dis2\r\n");
    printf("arr addr         val\r\n" );
    for(i = 7; i >= 0; --i) {
        printf("[%d] 0x%X: 0x%X\r\n", i, (int)&arr[i], arr[i]);
    }
    printf("r0(return value):0x%X\r\n", ret); 
    printf("-------------------------------------\r\n"); 
}

extern int* us_cnt;
extern int* us_r_idx;
extern int* us_w_idx;
int idle_task_entry(void* param) {
    unsigned long i = 0;
    while(1){
        //printf("us c:%d, w:%d, r:%d, rdy_tsk_cnt:%d\r\n", *us_cnt, *us_w_idx, *us_w_idx, g_tsk_mgr.ready_task_list.tsk_cnt);
        while(i < 0xFFFF) {
            i++;
        }
        i = 0;
    }
}

int tsk_enable_schedule(int tsk_id) {
    //TODO
    return 0;
}

int tsk_disable_schedule(int tsk_id) {
    //TODO
    return 0;
}

int get_current_tsk_id() {
    return g_current_tsk_id;
}

int tm_on_swi_reshedule_tsk() {
    //TODO
    return 0;
}

void init_task() {
    int i = 0;
    int tsk_id_idle;

    memset(&g_tsk_mgr, 0, sizeof(g_tsk_mgr));
    for(i = 1; i < MAX_TASK_COUNT; ++i) {
        g_tsk_mgr.arr[i].is_first_run = 1;
        g_tsk_mgr.arr[i].priority = 0xFF;//lowest
        g_tsk_mgr.arr[i].tsk_st = ST_TASK_INVALID;
        g_tsk_mgr.ready_task_list.task_list[i] = (unsigned char)0xFF;
        g_tsk_mgr.unready_task_list.task_list[i] = (unsigned char)0xFF;
    }

    tsk_id_idle = tsk_new(120, idle_task_entry, 0, "idle tsk"); //低优先级

    //这里指定tsk_id_idle没有特别的意义,但必须要指定一个有效的坑
    //比如这里指定为0,在第一次调度时,g_tsk_mgr.arr[0].r被pre_main的whild(1)处的现场填充
    //而在main被调度后,上一个TIME1中断后,g_tsk_mgr.arr[0].r将被main函数的现场覆盖
    g_pcurrent_regs = &g_tsk_mgr.arr[tsk_id_idle].r;
    g_current_tsk_id = tsk_id_idle;
    
    //tsk_id_usend = tsk_new(200, uart0_send_tsk_entry, 0, "uart send task soon unused.."); //高优先级

    tsk_run(tsk_id_idle);
}

int tsk_new(int priority, TASK_ENTRY_FUN entry, void* tsk_param, const char* tsk_name) {
    int i = 0;
    for(; i < sizeof(g_tsk_mgr.arr) / sizeof(g_tsk_mgr.arr[0]); ++i) {
        if(ST_TASK_INVALID == g_tsk_mgr.arr[i].tsk_st) {
            g_tsk_mgr.arr[i].entry = entry;
            g_tsk_mgr.arr[i].is_first_run = 1;
            g_tsk_mgr.arr[i].priority = priority;
            g_tsk_mgr.sum_prio += priority;
            g_tsk_mgr.arr[i].scheduled_cnt = 1;
            g_tsk_mgr.sum_scheduled_cnt += 1;
            g_tsk_mgr.arr[i].tsk_param = tsk_param;            
            g_tsk_mgr.arr[i].tsk_st = ST_TASK_WAIT_RUN;
            strcpyn(g_tsk_mgr.arr[i].tsk_name, tsk_name, MAX_LEN_TSK_DISCRIPT);
            g_tsk_mgr.valid_tsk_cnt++;
            return i;
        }
    }
    printf("[E]tsk_new failed. entry:0x%X\r\n", (int)entry);
    return -1;
}

//待放入syscal(swi中断)中做
int tsk_run(int tsk_id) {
    int i = 0;
    if(tsk_id < 0 || tsk_id >= (sizeof(g_tsk_mgr.arr) / sizeof(g_tsk_mgr.arr[0]))) {
        printf("[E]run task failed. invalid tsk_id:%d\r\n", tsk_id);
        return -1;
    }
    if(g_tsk_mgr.arr[tsk_id].tsk_st != ST_TASK_WAIT_RUN) {
        printf("[E]run task failed.tsk_id:%d invalid task state:%d\r\n", tsk_id, g_tsk_mgr.arr[i].tsk_st);
        return -2;
    }

    g_tsk_mgr.arr[tsk_id].tsk_st = ST_TASK_RUNNING;
    g_tsk_mgr.ready_task_list.task_list[g_tsk_mgr.ready_task_list.tsk_cnt++] = tsk_id;
    return 0;
}

int tsk_stop(int tsk_id) {
    return 0; //TODO
}

int sleep(int ms) {
    CALL_SWI(SWI_RESCHEDULE_TASK);
    return 0;
}

unsigned short get_next_task_id() {
    static unsigned long ticker = 0;
   
    ticker++;
    if(ticker >= g_tsk_mgr.ready_task_list.tsk_cnt) {
       ticker = 0; 
    }
    g_tsk_mgr.arr[ticker].scheduled_cnt++;
    g_tsk_mgr.sum_scheduled_cnt++;
    return ticker;
    //return ticker % g_tsk_mgr.ready_task_list.tsk_cnt; //大数的模运算,慎之! 用在调度中,直接死掉,原因不明...
}

void show_schedule_status() {
    static unsigned int j = 0;
    
    if(j < 20) set_led(LED_3, LED_ON);
    else set_led(LED_3, LED_OFF);

    if(++j >= 300) j = 0;
}

void show_schedule_status4() {
    static unsigned int j = 0;
    
    if(j < 20) {
        set_led(LED_4, LED_ON);
    } else {
        set_led(LED_4, LED_OFF);
    }

    ++j;
    if(j >= 300) {
        j = 0;
    }
}

//[目前阶段]Task调度中不要使用printf,因为printf中有锁,可能造成死锁的情况
void tsk_schedule() {
    unsigned short idx = 0;
    show_schedule_status();
    idx = get_next_task_id();
    if (IVALID_TASK_ID != idx) {
        g_pcurrent_regs = &g_tsk_mgr.arr[idx].r;
        g_current_tsk_id = idx;
        if (1 == g_tsk_mgr.arr[idx].is_first_run) {
            g_tsk_mgr.arr[idx].is_first_run = 0;
            set_task_sp(0x34000000 - idx * 512 * 1024);
            //printf("[task entry]tid=%d, %s.\r\n", idx, g_tsk_mgr.arr[idx].tsk_name);
            g_tsk_mgr.arr[idx].entry(g_tsk_mgr.arr[idx].tsk_param);
        } else {
            restore_run(&g_tsk_mgr.arr[idx].r);
        }
    }

    while(1){}
}

void tsk_schedule2() {
    unsigned short idx = 0;
    show_schedule_status4();
    idx = get_next_task_id();
    
    if (IVALID_TASK_ID != idx) {
        g_pcurrent_regs = &g_tsk_mgr.arr[idx].r;
        g_current_tsk_id = idx;
        if (1 == g_tsk_mgr.arr[idx].is_first_run) {
            g_tsk_mgr.arr[idx].is_first_run = 0;
            set_task_sp(0x34000000 - idx * 512 * 1024);
            //printf("[task entry]tid=%d, %s.\r\n", idx, g_tsk_mgr.arr[idx].tsk_name);
            g_tsk_mgr.arr[idx].entry(g_tsk_mgr.arr[idx].tsk_param);
        } else {
            restore_run2(&g_tsk_mgr.arr[idx].r);
        }
    }

    while(1){}
}

int tm_dump_all_tsk_info() {
    int i;

    printfk("sum prio:\r\n", g_tsk_mgr.sum_prio);
    printfk("sum scheduled cnt:\r\n", g_tsk_mgr.sum_scheduled_cnt);
    
    for(; i < g_tsk_mgr.valid_tsk_cnt; ++i) {
        tm_dump_1tsk_info(i);
    }
    return 0;
}

int tm_tsk_info_brief() {
    int i = 0;
    printfk("[Task] Total:%d, running:%d, sleeping:%d, total schedule count:%d\r\n", g_tsk_mgr.valid_tsk_cnt, 2, 3, 4);
    printfk("[TID] [PIO] [STA] [SCNT] [TSK NAME]\r\n");

    for(; i < g_tsk_mgr.valid_tsk_cnt; ++i) {
        task_info* pt = &g_tsk_mgr.arr[i];
        printfk("%d  ", i);
        printfk("%d  ", pt->priority);
        printfk("%d  ", pt->tsk_st);
        printfk("%d  ", pt->scheduled_cnt);
        printfk("%s\r\n", pt->tsk_name);        
    }
    return 0;
}

int tm_dump_1tsk_info(int tsk_id) {
    if(tsk_id < 0 || tsk_id >= (sizeof(g_tsk_mgr.arr) / sizeof(g_tsk_mgr.arr[0]))) {
        printfk("[E]run task failed. invalid tsk_id:%d\r\n", tsk_id);
        return -1;
    }
    
    task_info* pt = &g_tsk_mgr.arr[tsk_id];
    printfk("  task name:%s\r\n", pt->tsk_name);
    printfk("    is first run:%d\r\n", pt->is_first_run);
    printfk("    priority:%d\r\n", pt->priority);
    printfk("    tsk_st:%d\r\n", pt->tsk_st);
    printfk("    prio_rate:%d\r\n", pt->prio_rate);
    printfk("    scheduled_cnt:%d\r\n", pt->scheduled_cnt);
    printfk("    entry:0x%X\r\n", pt->entry);
    printfk("    tsk_param:0x%X\r\n", pt->tsk_param);
    printfk("      r0:0x%X\r\n", pt->r.r0);
    printfk("      r1:0x%X\r\n", pt->r.r1);
    printfk("      r2:0x%X\r\n", pt->r.r2);
    printfk("      r3:0x%X\r\n", pt->r.r3);
    printfk("      r4:0x%X\r\n", pt->r.r4);
    printfk("      r5:0x%X\r\n", pt->r.r5);
    printfk("      r6:0x%X\r\n", pt->r.r6);
    printfk("      r7:0x%X\r\n", pt->r.r7);
    printfk("      r8:0x%X\r\n", pt->r.r8);
    printfk("      r9:0x%X\r\n", pt->r.r9);
    printfk("      r10:0x%X\r\n", pt->r.r10);
    printfk("      r11:0x%X\r\n", pt->r.r11);
    printfk("      r12:0x%X\r\n", pt->r.r12);
    printfk("      r13(sp):0x%X\r\n", pt->r.sp_r13);
    printfk("      r14(lr):0x%X\r\n", pt->r.lr_r14);
    printfk("      r15(pc):0x%X\r\n", pt->r.pc_r15);
    printfk("      cpsr:0x%X\r\n", pt->r.cpsr);
    
    return 0;
}


























